package ro.cityprojects.adminloc.controller;

import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import ro.cityprojects.adminloc.entities.BaseEntity;
import ro.cityprojects.adminloc.exceptions.ValidationException;

import javax.validation.Valid;
import java.util.Set;

/**
 * Controller used to specify the API contract operations
 *
 * @author bogdan.solga
 *
 * Date: 14.06.2013, time: 15:38
 */
public abstract class BaseController<ID extends Number, Entity extends BaseEntity> {

    // Create operations
    @RequestMapping(method = RequestMethod.POST)
    public void assertAndSave(@Valid @RequestBody Entity entity) throws ValidationException {
        Assert.notNull(entity, "Invalid " + entity.getType() + ", cannot be null");

        save(entity);
    }

    public abstract void save(Entity entity) throws ValidationException;

    @RequestMapping(method = RequestMethod.POST, value = "/all")
    public void saveAll(@Valid @RequestBody Set<Entity> entities) throws ValidationException {
        Assert.notEmpty(entities, "Invalid set of " + entities.iterator().next().getType() + " s, cannot be empty");

        for (Entity entity : entities) {
            assertAndSave(entity);
        }
    }

    // Read operations
    @RequestMapping(method = RequestMethod.GET, value = "/{id}")
    public @ResponseBody String assertAndGet(@PathVariable ID id, @RequestParam(required = false) String includedFields) {
        Assert.notNull(id, "Invalid primary key, cannot be null");

        return get(id, includedFields);
    }

    public abstract @ResponseBody String get(@PathVariable ID id, @RequestParam(required = false) String includedFields);

    @RequestMapping(method = RequestMethod.GET, value = "/all")
    public abstract @ResponseBody String get(@RequestParam(required = false) String name,
                                             @RequestParam(required = false) String value,
                                             @RequestParam(required = false) String includedFields);

    // Update operations
    @RequestMapping(method = RequestMethod.PUT, value = "/{id}")
    public void assertAndUpdate(@Valid @RequestBody Entity entity, @PathVariable ID id) {
        Assert.notNull(entity, "Invalid " + entity.getType() + ", cannot be null");

        update(entity, id);
    }

    public abstract void update(Entity entity, ID id);

    @RequestMapping(method = RequestMethod.PUT, value = "/all")
    public void updateAll(@Valid @RequestBody Set<Entity> entities) {
        Assert.notEmpty(entities, "Invalid set of " + entities.iterator().next().getType() + " s, cannot be empty");

        for (Entity entity : entities) {
            //TODO: send the primary keys as an array
            assertAndUpdate(entity, null);
        }
    }

    // Delete operations
    @RequestMapping(method = RequestMethod.DELETE, value = "/{id}")
    public void assertAndDelete(@PathVariable ID id) {
        Assert.notNull(id, "Invalid primary key, cannot be null");

        delete(id);
    }

    public abstract void delete(ID id);

    @RequestMapping(method = RequestMethod.DELETE, value = "/all")
    public void deleteAll(Set<ID> primaryKeys) {
        Assert.notEmpty(primaryKeys, "Invalid set of primary keys, cannot be null");

        for (ID primaryKey : primaryKeys) {
            assertAndDelete(primaryKey);
        }
    }

    @RequestMapping(method = RequestMethod.GET, value = "/meta")
    public abstract @ResponseBody String getMetadata();
}
